package sdmakerz_v1.coresyntax.generator

import java.util.ArrayList
import java.util.List
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import sdmakerz_v1.coresyntax.generator.interfaces.IContext
import sdmakerz_v1.coresyntax.generator.interfaces.IInputData
import sdmakerz_v1.coresyntax.generator.interfaces.IConfigData
import sdmakerz_v1.coresyntax.generator.interfaces.IDiagramRefData
import sdmakerz_v1.coresyntax.generator.interfaces.IInterfaceData
import sdmakerz_v1.coresyntax.sDMakerV1.Diagram
import sdmakerz_v1.coresyntax.io.SDMakerIO
import sdmakerz_v1.coresyntax.sDMakerV1.Obj
import sdmakerz_v1.coresyntax.sDMakerV1.Call
import sdmakerz_v1.coresyntax.sDMakerV1.Reference
import sdmakerz_v1.coresyntax.sDMakerV1.Config
import sdmakerz_v1.coresyntax.SDMakerV1Utils

/**
 * @author pstl-team
 */
class ContextImpl implements IContext {
    /** La resource qui contient les donnees du fichier sdmaker traite */
	private IInputData inputData;
	
	/** Les donnees de configuration du contexte */
	private IConfigData configData;
	
	/** Les donnees pour le renommage des variables */
	private IDiagramRefData diagramRefData;

	/** Les donnees liant les interfaces et leurs instances */
	private List<IInterfaceData> interfacesDatas;
	
	private Diagram contextDiagram;
     
     
    /* ******************** *
	 * *** INITIALIZERS *** *
	 * ******************** */
	 
    override public void init(){
   		var List<String> types= findAllTypeQualifiedName;
		var SDMakerIO sdmakerIO= new SDMakerIO()
		var List<IInputData> inputDatas= sdmakerIO.allInputs;
		
		this.inputData= null;
		this.configData= null;
		this.diagramRefData= null;
		this.contextDiagram= null;
		this.interfacesDatas= new ArrayList<IInterfaceData>();

		for(String type : types){
			addNewType(type);
		}
		
		/*
		 * An id can be reuse for differents items in differents files,
		 * that's why it's allow to reinit IDs (for avoid int overflow)
		 * but interface ids must never be use agains. The following lines
		 * ensure that.
		 */
		SDMakerV1IDProvider::
			setMinimalIDNumber(SDMakerV1IDProvider::computeID)
		SDMakerV1IDProvider::reinitIDs
		
		for(IInputData inputDTmp : inputDatas){
		
			this.inputData= inputDTmp;
		
			for(Obj obj : inputData.input
									.allContents
									.toIterable
									.filter(typeof(Obj))){
				bindObjWithInterface(obj);
			}
			
			for(Call call : inputData.input
									.allContents
									.toIterable
									.filter(typeof(Call))){
				bindCallWithInterface(call);
			}
		}
		
		cleanContext;		
   	}
	
	
	
	/* *************** *
	 * *** SETTERS *** *
	 * *************** */
	 
	override public 
	void setInput(IInputData inputData){
		this.inputData= inputData
	}
	
	override public 
	void setContextDiagram(Diagram contextDiagram){
		this.contextDiagram= contextDiagram
	}
	
	override public 
	void setConfigData(IConfigData configData){
		this.configData= configData
	}
	
	override public 
	void setDiagramRefData(IDiagramRefData diagramRefData){
		this.diagramRefData= diagramRefData
	}

	override public void cleanContext(){
		this.inputData= null;
		this.contextDiagram= null;
		this.configData= null;
		this.diagramRefData= null;
	}

  	def void 
	addNewType(String typeQualifiedName){
  		var IInterfaceData iid= new InterfaceDataImpl();

  		iid.init();
  		iid.setName(typeQualifiedName);
  		iid.setInterfaceID(
  			typeQualifiedName.replaceAll("\\.", "_")+
  			"Class_id"+SDMakerV1IDProvider::computeID)
  		interfacesDatas.add(iid);
  	}
  	
  	def public 
  	void bindObjWithInterface(Obj obj){
  		
  		var List<String> types= 
  			SDMakerV1Utils::findMatchingQualifiedNames(inputData.getInput, obj.type.identifier);
  
      	if(types.size == 1){
    		for(IInterfaceData iid : interfacesDatas){
    			if(iid.getQualifiedName.compareTo(types.get(0))==0){
    				iid.addObj(obj);
    			}
    		}
    	}
  	}
  
  	def public 
  	void bindCallWithInterface(Call c){
  		var EObject eo= SDMakerV1Utils::findContainer(c, typeof(Diagram));
    	var Diagram d= eo as Diagram;
    	var Obj called= null;
    	var List<String> types= null;
    	var IInterfaceData iid= null;
    
  		if(d!=null){
  			for(Obj obj : d.eAllContents.toIterable.filter(typeof(Obj))){
  				if(obj.name.compareTo(c.called)==0){
  					called= obj;
  				}
  			}
  		
  			if(called != null){
  				types= SDMakerV1Utils::findMatchingQualifiedNames(
  						inputData.getInput, called.type.identifier);
  			
  				// != 1 veut dire non trouvee ou conflit
  				if(types.size == 1){
  					iid= getInterfaceDataByName(types.get(0));
  				}
  			
  				if(iid != null){
  					iid.addCall(inputData, c);
  				}
  			}
  		}
	}  
	
	
	/* *************** *
	 * *** GETTERS *** *
	 * *************** */
	 
	override IInputData getInputData() {
		return inputData;
	}
	
	override IConfigData getConfigData(){
		return configData;
	}
	
	override IDiagramRefData getDiagramRefData(){
		return diagramRefData;
	}
	
	override List<IInterfaceData> getInterfacesDatas(){
		return interfacesDatas;
	}
	
	override IInterfaceData getInterfaceDataByName(String name){
			
	    var IInterfaceData foundedInterfaceDatas= null;
	    var String qualifiedName= null;
	    var List<String> matchingQualifiedName= 
	    	SDMakerV1Utils::findMatchingQualifiedNames(inputData.input, name);
                
                
        if(matchingQualifiedName.size == 1){
        	
        	qualifiedName= matchingQualifiedName.get(0);

	    	for(interfaceDatas: interfacesDatas){
	        	if(interfaceDatas.getQualifiedName.compareTo(qualifiedName)==0){
	            	foundedInterfaceDatas= interfaceDatas;
	        	}
	    	}
	    }
	    
	    return foundedInterfaceDatas;
	}
	
	override IInterfaceData getInterfaceDataByObject(Obj obj){
	    var IInterfaceData iid= null;

	    for(interfaceData: interfacesDatas){
	        if(interfaceData.contains(obj)){
	            iid= interfaceData
	        }
	    }
	    
	    return iid;
	}
	
	override IInterfaceData 
	getInterfaceDataByObjectName(String objName){
		var IInterfaceData iid= null;
		var Obj obj= null;
		
		if(contextDiagram!=null){
			for(Obj objTmp : contextDiagram.eAllContents
							  			   .toIterable
							  			   .filter(typeof(Obj))){
							  	
				if(objTmp.name.compareTo(objName)==0){
					obj= objTmp;
				}
			}
		
			if(obj!=null){
				iid= getInterfaceDataByObject(obj);
			}	
		}
	
		return iid;
	}
	
	override Diagram findDiagram(Reference diagRef){
        var SDMakerIO sdmakerIO= new SDMakerIO();
		var Diagram diag= 
		      sdmakerIO.findDiagramWithImport(diagRef);
        
        return diag;
    }
    
    override Resource findDiagramInput(Reference diagRef){
        var SDMakerIO sdmakerIO= new SDMakerIO();
		var Resource diagInput= 
		      sdmakerIO.findDiagramWithImport(diagRef).eResource;
        
        return diagInput;
    }
    
    override Config findConfig(Reference confRef){
        var SDMakerIO sdmakerIO= new SDMakerIO();
		var Config config= 
		      sdmakerIO.findConfigWithImports(inputData.getInput, confRef);
        
        return config;
  	}
    
    def List<String> findAllTypeQualifiedName(){
		var SDMakerIO sdmakerIO= new SDMakerIO();
        var List<IInputData> inputs= sdmakerIO.allInputs;
        var List<String> types= new ArrayList<String>();

        for(IInputData inputDTmp : inputs){
        	var inputTmp = inputDTmp.getInput
            for(obj : inputTmp.allContents.toIterable.filter(typeof(Obj))){
                var List<String> matchingQualifiedName= SDMakerV1Utils::findMatchingQualifiedNames(inputTmp, obj.type.identifier);
                
                
                if(matchingQualifiedName.size == 1){
                	if(SDMakerV1Utils::contains(types, matchingQualifiedName.get(0))==false){
                    	types.add(matchingQualifiedName.get(0));
                    }
                }
            }
        }
        
        return types;
	}
}